package com.design.found.singleton;

/**
 * 23种设计模式之:单例模式
 * 懒汉式单例
 * 该模式的特点是类加载时没有生成单例，只有当第一次调用 getlnstance 方法时才去创建这个单例。
 *
 *
 *
 */
public class Student {

    //私有化构造函数
    private Student(){ }


    private static Student student = null;

    /**
     * 2.普通的懒汉式 (线程不安全，不可用)
     * 这是懒汉式中最简单的一种写法，只有在方法第一次被访问时才会实例化，达到了懒加载的效果。
     * 但是这种写法有个致命的问题，就是多线程的安全问题。假设对象还没被实例化，然后有两个线程同时访问，
     * 那么就可能出现多次实例化的结果，所以这种写法不可采用。
     */
    public static Student getInstance2() {
        if (student == null) {
            student = new Student();
        }
        return student;
    }

    public static void main(String[] args) {
        System.out.println(getInstance2());
        System.out.println(getInstance3());
    }

    /**
     * 3.同步方法的懒汉式 (可用)
     * 这种写法是对getInstance()加了锁的处理，保证了同一时刻只能有一个线程访问并获得实例，但是缺点也很明显，
     * 因为synchronized是修饰整个方法，每个线程访问都要进行同步，而其实这个方法只执行一次实例化代码就够了，
     * 每次都同步方法显然效率低下，为了改进这种写法，就有了下面的双重检查懒汉式。
     */
    public static synchronized Student getInstance3() {
        if (student == null) {
            student = new Student();
        }
        return student;
    }


    /**
     * 4.双重检查懒汉式 (可用，推荐)
     *这种写法用了两个if判断，也就是Double-Check，并且同步的不是方法，而是代码块，效率较高，是对第三种写法的改进。
     * 为什么要做两次判断呢？这是为了线程安全考虑，还是那个场景，对象还没实例化，
     * 两个线程A和B同时访问静态方法并同时运行到第一个if判断语句，这时线程A先进入同步代码块中实例化对象，
     * 结束之后线程B也进入同步代码块，如果没有第二个if判断语句，那么线程B也同样会执行实例化对象的操作了。
     */
    public static Student getInstance4() {
        if (student == null) {
            synchronized (Student.class) {
                if (student == null) {
                    student = new Student();
                }
            }
        }
        return student;
    }


    /**
     * 5.静态内部类 (可用，推荐)
     * 这是很多开发者推荐的一种写法，这种静态内部类方式在Singleton类被装载时并不会立即实例化，而是在需要实例化时，
     * 调用getInstance方法，才会装载SingletonInstance类，从而完成对象的实例化。
     * 同时，因为类的静态属性只会在第一次加载类的时候初始化，也就保证了SingletonInstance中的对象只会被实例化一次，
     * 并且这个过程也是线程安全的。
     */
    public static Student getInstance5() {
        return StudentInstance.student;
    }

    private static class StudentInstance {
        private static final Student student = new Student();
    }


    /**
     * 6. 枚举方法（线程安全）
     * Effective Java作者Josh Bloch 提倡的方式，在我看来简直是来自神的写法。解决了以下三个问题：
     * (1)自由序列化。
     * (2)保证只有一个实例。
     * (3)线程安全。
     *  如果我们想调用它的方法时，仅需要以下操作
     * SingletonEnum.getInstance();
     */
    static enum SingletonEnum {
        INSTANCE;
        private Student student;

        private SingletonEnum(){
            student = new Student();
        }
        public Student getInstnce(){
            return student;
        }
        public static Student getInstance(){
            return SingletonEnum.INSTANCE.getInstnce();
        }

    }
}